'''
An RPG Modification for Source games using Source Python.

    Copyright (C) 2012 Steven Hartin

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Created on 22 Dec 2012

@author: Steven Hartin
@file: ../sourcerpg/database/BaseDatabase.py
'''

from collections import abc
from time import time

from players.helpers import index_from_playerinfo
from players.entity import PlayerEntity
from Source import Player

from sourcerpg.database.cache import DataCache
from sourcerpg.database.parser import Parser
from sourcerpg.config import config
from sourcerpg.skills import skills


class BaseDatabase(DataCache):
    """Abstract class for all accessors / setters for the data in SourceRPG

    Since SourceRPG will support multiple database APIs, we can't execute
    direct raw commands from the plugin itself as the syntax of the commands
    may be different. Therefore, everything will be supported through
    accessors / setters. To ensure consistency throughout the APIs, this
    abstract class will ensure all abstract methods are inherited and used.

    """
    __metaclass__ = abc.ABCMeta

    def __init__(self, parser_file=None, meta_file=None):
        # Base data cache key
        super(BaseDatabase, self).__init__()
        self["players"] = {}
        self.parser = None
        if parser_file is not None and meta_file is not None:
            # Create the parser and parse the database installation file
            self.parser = Parser(parser_file, meta_file, self)
        self.update_cache()

    @abc.abstractmethod
    def execute_raw_command(self, command):
        pass

    @abc.abstractmethod
    def update_player_cache(self, player):
        pass

    @abc.abstractmethod
    def commit_cache(self):
        pass

    @abc.abstractmethod
    def delete_inactive_players_api(self, time):
        pass

    def update_cache(self):
        """Updates the cache to the latest database entry

        This method's name is slightly misleading. Although updating the cache,
        we first perform a cache commit to ensure that any player who has left
        the player gets their stats sent to the database. This update cache
        essentially wipes out the cache, and starts again from the database
        entries with the player's on the server. Before we can do that, we must
        ensure that no data is lost. If nothing has altered since the last
        commit, then the dirty / new flags will be set anyway.

        """
        # First ensure that any players which have left the server have their
        # data saved
        self.commit_cache()

        # Clear the current cache
        self["players"].clear()

        for player_info in Player.Players():
            index = index_from_playerinfo(player_info)
            player = PlayerEntity(index)

            self.init_player_cache(player)

    def init_player_cache(self, player):
        """Sets up the cache for the player.

        This should only be used if the player has had no cache entry before
        (i.e. they are a new player). This will initialise the player to the
        default cache values

        @param PlayerEntity player The entity of the player to initialise

        """
        assert type(player) == PlayerEntity
        uniqueid = player.uniqueid
        self["players"][uniqueid] = {}
        self["players"][uniqueid]["xp"] = 0
        self["players"][uniqueid]["level"] = 1
        self["players"][uniqueid]["credits"] = 5
        self["players"][uniqueid]["skills"] = {}
        # Set to -1 as we're not using an ID, and None is treated as being
        # 'new'
        self["players"][uniqueid]["skills"].id = -1

        for skill_name in skills:
            self["players"][uniqueid]["skills"][skill_name] = {}
            self["players"][uniqueid]["skills"][skill_name]["level"] = 0

        self["players"][uniqueid]["name"] = player.name
        self["players"][uniqueid]["lastconnected"] = time()
        self.update_player_cache(player)

    def delete_inactive_players(self):
        """Deletes all inactive players who last connected a long time ago"""
        inactive_counter = (
            config["database"]["inactivity_timer"]["months"],
            config["database"]["inactivity_timer"]["weeks"],
            config["database"]["inactivity_timer"]["days"],
            config["database"]["inactivity_timer"]["hours"])
        inactive_timer = inactive_counter[0] * 60 * 60 * 24 * 7 * 28
        inactive_timer += inactive_counter[1] * 60 * 60 * 24 * 7
        inactive_timer += inactive_counter[2] * 60 * 60 * 24
        inactive_timer += inactive_counter[3] * 60 * 60
        # Call the API equivilent of this
        self.delete_inactive_players_api(inactive_timer)
